package com.wssnail.oss.template;

import com.qiniu.common.QiniuException;
import com.qiniu.http.Client;
import com.qiniu.http.Response;
import com.qiniu.storage.*;
import com.qiniu.util.Auth;
import com.qiniu.util.Json;
import com.wssnail.oss.myenum.FileEnum;
import com.wssnail.oss.myenum.FileTypeEnum;
import com.wssnail.oss.utils.FileNameUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: 熟透的蜗牛
 * @CreateTime: 2022-07-11 14:33
 * @Description: 七牛云上传图片
 * @Version: 1.0
 */
@Slf4j
public class QiNiuOssTemplate extends OssTemplate {

    private String bucketName;
    private String tempPath;
    private String domain;
    private Auth auth;
    private BucketManager bucketManager;
    private UploadManager uploadManager;
    private int blockSize;
    private String urlPrefix;


    public QiNiuOssTemplate(String bucketName, String tempPath, String domain, Auth auth, BucketManager bucketManager,
                            UploadManager uploadManager, int blockSize, String urlPrefix) {
        this.bucketName = bucketName;
        this.tempPath = tempPath;
        this.domain = domain;
        this.auth = auth;
        this.bucketManager = bucketManager;
        this.uploadManager = uploadManager;
        this.blockSize = (blockSize == 0 ? 0 : blockSize);
        this.urlPrefix = urlPrefix;
    }

    @Override
    public Map upload(MultipartFile file) {
        Map resultMap = new HashMap();
        try {
            if (file.isEmpty()) {
                resultMap.put("msg", FileEnum.EMPTY_FILE.getMsg());
                resultMap.put("code", FileEnum.EMPTY_FILE.getCode());
                return resultMap;
            }
            FileTypeEnum fileTypeEnum = checkFileType(file.getInputStream());
            if (fileTypeEnum == null) {
                resultMap.put("code", FileEnum.FILE_TYPE_ERROR.getCode());
                resultMap.put("msg", FileEnum.FILE_TYPE_ERROR.getMsg());
                return resultMap;
            }
            File temp = new File(tempPath);
            String originalFilename = file.getOriginalFilename();
            //uploadFile是MultipartFile对象
            file.transferTo(temp);
            //获取到文件绝对路径
            StringBuffer stringBuffer = new StringBuffer();
            String suffixName = originalFilename.substring(originalFilename.lastIndexOf(".")); //获取文件后缀
            String newFileName = stringBuffer.append(FileNameUtils.getString(10)).append(suffixName).toString();
            Response resultResponse = uploadManager.put(temp.getPath(), newFileName, getUpToken());
            if (FileEnum.SUCCESS.getCode().equals(resultResponse.statusCode)) {
                resultMap = Json.decode(resultResponse.bodyString(), Map.class);
                String name = (String) resultMap.get("key");
                resultMap.put("url", domain + name);
            } else {
                resultMap.put("code", FileEnum.FAIL.getCode());
                resultMap.put("msg", FileEnum.FAIL.getMsg());
            }
            resultMap.remove("hash");
            temp.delete();
        } catch (IOException e) {
            e.printStackTrace();
            log.info(">>>>>>>>>>>>>>>>>>>>上传失败");
        }
        return resultMap;
    }

    @Override
    public Map deleteFile(String fileName) {
        Map resultMap = new HashMap<String, Object>();
        try {
            Response deleteResponse = bucketManager.delete(bucketName, fileName);
            if (FileEnum.SUCCESS.getCode().equals(deleteResponse.statusCode)) {
                resultMap.put("code", FileEnum.SUCCESS.getCode());
                resultMap.put("msg", FileEnum.SUCCESS.getMsg());
            } else {
                resultMap.put("code", FileEnum.FAIL.getCode());
                resultMap.put("msg", FileEnum.FAIL.getMsg());
            }
        } catch (QiniuException e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    @Override
    public String downLoadFile(HttpServletResponse response, String fileName) {
        try {
            StringBuffer stringBuffer = new StringBuffer(domain);
            String downloadUrl = auth.privateDownloadUrl(stringBuffer.append(fileName).toString(), 3600);
            return downloadUrl;
        } catch (Exception e) {
            log.info(">>>>>>>>>>>>>>>>>>下载文件异常");
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Map chunkFile(MultipartFile file) {
        Map resultMap = new HashMap();
        try {
            if (file.isEmpty()) {
                resultMap.put("msg", FileEnum.EMPTY_FILE.getMsg());
                resultMap.put("code", FileEnum.EMPTY_FILE.getCode());
                return resultMap;
            }
            FileTypeEnum fileTypeEnum = checkFileType(file.getInputStream());
            if (fileTypeEnum == null) {
                resultMap.put("code", FileEnum.FILE_TYPE_ERROR.getCode());
                resultMap.put("msg", FileEnum.FILE_TYPE_ERROR.getMsg());
                return resultMap;
            }
            File temp = new File(tempPath);
            String originalFilename = file.getOriginalFilename();
            file.transferTo(temp);
            //获取到文件绝对路径
            StringBuffer stringBuffer = new StringBuffer();
            String suffixName = originalFilename.substring(originalFilename.lastIndexOf(".")); //获取文件后缀
            String newFileName = stringBuffer.append(FileNameUtils.getString(10)).append(suffixName).toString();
            //开始分片上传
            long fileSize = temp.length() / 1024; // 单位： k
            fileSize *= 1024; // 单位： B
            int defaultPartSize = 1024 * 1024 * blockSize;
            RandomAccessFile randomAccessFile = null;
            try {
                randomAccessFile = new RandomAccessFile(temp, "r");
            } catch (IOException e) {
                e.printStackTrace();
            }
            String token = getUpToken();
            Configuration configuration = new Configuration();
            Client client = new Client(configuration);
            // 1. init upload
            String uploadId = null;
            ApiUploadV2InitUpload initUploadApi = new ApiUploadV2InitUpload(client);
            ApiUploadV2InitUpload.Request initUploadRequest = new ApiUploadV2InitUpload.Request(urlPrefix, token)
                    .setKey(newFileName);
            try {
                ApiUploadV2InitUpload.Response initUploadResponse = initUploadApi.request(initUploadRequest);
                uploadId = initUploadResponse.getUploadId();
            } catch (QiniuException e) {
                e.printStackTrace();
            }
            // 2. 上传文件数据
            List<Map<String, Object>> partsInfo = new ArrayList<>();
            long partOffset = 0; // 块在文件中的偏移量
            int partNumber = 1; // part num 从 1 开始
            while (partOffset < fileSize) {
                // 1.2.1 读取 part 数据
                long partSize = fileSize - partOffset;
                if (partSize > defaultPartSize) {
                    partSize = defaultPartSize;
                }
                byte[] partData = getUploadData(randomAccessFile, partOffset, (int) partSize);

                // 1.2.2 上传 part 数据
                ApiUploadV2UploadPart uploadPartApi = new ApiUploadV2UploadPart(client);
                ApiUploadV2UploadPart.Request uploadPartRequest = new ApiUploadV2UploadPart.Request(urlPrefix, token, uploadId, partNumber)
                        .setKey(newFileName)
                        .setUploadData(partData, 0, partData.length, null);
                try {
                    ApiUploadV2UploadPart.Response uploadPartResponse = uploadPartApi.request(uploadPartRequest);
                    String etag = uploadPartResponse.getEtag();
                    Map<String, Object> partInfo = new HashMap<>();
                    partInfo.put(ApiUploadV2CompleteUpload.Request.PART_NUMBER, partNumber);
                    partInfo.put(ApiUploadV2CompleteUpload.Request.PART_ETG, etag);
                    partsInfo.add(partInfo);
                    log.info(">>>>>>>>>>>>>>>>>>>upload part:{}", uploadPartResponse.getResponse());
                } catch (QiniuException e) {
                    e.printStackTrace();
                    break;
                }
                // 1.2.3 计算下一个 part 的 number
                partNumber += 1;
                partOffset += partSize;
            }
            // 获取上传的 part 信息
            Integer partNumberMarker = null;
            List<Map<String, Object>> listPartInfo = new ArrayList<>();
            while (true) {
                ApiUploadV2ListParts listPartsApi = new ApiUploadV2ListParts(client);
                ApiUploadV2ListParts.Request listPartsRequest = new ApiUploadV2ListParts.Request(urlPrefix, token, uploadId)
                        .setKey(newFileName)
                        .setPartNumberMarker(partNumberMarker);
                try {
                    ApiUploadV2ListParts.Response listPartsResponse = listPartsApi.request(listPartsRequest);
                    partNumberMarker = listPartsResponse.getPartNumberMarker();
                    listPartInfo.addAll(listPartsResponse.getParts());
                    // 列举结束
                    if (partNumberMarker == 0) {
                        break;
                    }
                } catch (QiniuException e) {
                    e.printStackTrace();
                    break;
                }
            }
            // 3. 组装文件
            String fooValue = "foo-Value";
            Map<String, Object> customParam = new HashMap<>();
            customParam.put("x:foo", fooValue);
            ApiUploadV2CompleteUpload completeUploadApi = new ApiUploadV2CompleteUpload(client);
            ApiUploadV2CompleteUpload.Request completeUploadRequest = new ApiUploadV2CompleteUpload.Request(urlPrefix, token, uploadId, partsInfo)
                    .setKey(newFileName)
                    .setCustomParam(customParam);
            try {
                ApiUploadV2CompleteUpload.Response completeUploadResponse = completeUploadApi.request(completeUploadRequest);
                log.info(">>>>>>>>>上传完成,文件名为:{}", completeUploadResponse.getKey());
                if (FileEnum.SUCCESS.getCode().equals(completeUploadResponse.getResponse().statusCode)) {
                    resultMap = Json.decode(completeUploadResponse.getResponse().bodyString(), Map.class);
                    String name = (String) resultMap.get("key");
                    resultMap.put("url", domain + name);
                } else {
                    resultMap.put("code", FileEnum.FAIL.getCode());
                    resultMap.put("msg", FileEnum.FAIL.getMsg());
                }
                resultMap.remove("hash");
                resultMap.remove("x:foo");
            } catch (QiniuException e) {
                e.printStackTrace();
            }
            temp.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    /*
     * @description:获取七牛云的token
     * @Author: 熟透的蜗牛
     * @date: 2022/6/23 13:37
     * @return: java.lang.String
     **/
    private String getUpToken() {
        return auth.uploadToken(bucketName);
    }

    private byte[] getUploadData(RandomAccessFile file, long offset, int size) {
        byte[] uploadData = new byte[size];
        try {
            file.seek(offset);
            int readSize = 0;
            while (readSize != size) {
                int s = 0;
                try {
                    s = file.read(uploadData, readSize, size - readSize);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (s >= 0) {
                    readSize += s;
                } else {
                    break;
                }
            }
        } catch (IOException e) {
            uploadData = null;
        }
        return uploadData;
    }
}
